﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;

namespace XMLPathNavigator
{
    public partial class XMLPathNavigatorForm : Form
    {
        public XMLPathNavigatorForm()
        {
            InitializeComponent();
        }

        private string fileName;

        private XNode current;
        private XDocument document;
        private TreeNode tree;

        private void XMLPathNavigatorForm_Load(object sender, EventArgs e)
        {
            ControlsLock();
        }

        private void openButton_Click(object sender, EventArgs e)
        {
            DialogResult result;

            using (OpenFileDialog fileChooser = new OpenFileDialog())
            {
                fileChooser.Filter = "XML Files (*.xml)|*.xml";
                fileChooser.FilterIndex = 0;
                fileChooser.DefaultExt = "xml";

                result = fileChooser.ShowDialog();
                fileName = fileChooser.FileName;
            }

            if (result == DialogResult.OK)
            {
                if (fileName == string.Empty)
                {
                    MessageBox.Show("Error reading file", "File Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Error);
                }
                else if (Path.GetExtension(fileName) != ".xml")
                {
                    MessageBox.Show("Please choose a different file with .xml extension", "Wrong file extension",
                        MessageBoxButtons.OKCancel, MessageBoxIcon.Error);
                }
                else
                {
                    XLoad();
                }
            }
        }

        private void XLoad()
        {
            ControlsLock();
            openTextBox.Text = fileName;

            document = XDocument.Load(fileName);

            current = document;
            tree = new TreeNode(NodeText(current));
            pathTreeView.Nodes.Add(tree);
            RefreshTreeView();
        }

        private string NodeText(XNode node)
        {
            // different node types are displayed in different ways
            switch (node.NodeType)
            {
                case XmlNodeType.Document:
                    // display the document root
                    return "Document root";
                case XmlNodeType.Element:
                    // represent node by tag name
                    return '<' + (node as XElement).Name.LocalName + '>';
                case XmlNodeType.Text:
                    // represent node by text stored in Value property
                    return (node as XText).Value;
                case XmlNodeType.Comment:
                    // represent node by comment text
                    return (node as XComment).ToString();
                case XmlNodeType.ProcessingInstruction:
                    // represent node by processing-instruction text
                    return (node as XProcessingInstruction).ToString();
                default:
                    // all nodes in this example are already covered;
                    // return a reasonable default value for other nodes
                    return node.NodeType.ToString();
            } // end switch
        }

        private void RefreshTreeView()
        {
            pathTreeView.ExpandAll();
            pathTreeView.Refresh();
            pathTreeView.SelectedNode = tree;
        }

        private void ControlsLock()
        {
            foreach (Control control in Controls)
            {
                control.Enabled = !control.Enabled;
            }

            openTextBox.Enabled = true;
            openButton.Enabled = true;
        }

        private void firstChildButton_Click(object sender, EventArgs e)
        {
            var container = current as XContainer;

            if (container != null && container.Nodes().Any())
            {
                current = container.Nodes().First();

                var newNode = new TreeNode(NodeText(current));
                tree.Nodes.Add(newNode);
                tree = newNode;
                RefreshTreeView();
            }
            else
            {
                MessageBox.Show("Current node has no children.", "Warning",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void parentButton_Click(object sender, EventArgs e)
        {
            if (current.Parent != null)
                current = current.Parent;
            else
                current = current.Document;

            if (tree.Parent != null)
            {
                tree = tree.Parent;
                tree.Nodes.Clear();
                RefreshTreeView();
            }
        }


        private void previousButton_Click(object sender, EventArgs e)
        {
            if (current.PreviousNode != null)
            {
                current = current.PreviousNode;
                var treeParent = tree.Parent;
                treeParent.Nodes.Remove(tree);
                tree = treeParent.LastNode;
                RefreshTreeView();
            }
            else
            {
                MessageBox.Show("Current node is first sibling.", "Warning",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }


        private void nextButton_Click(object sender, EventArgs e)
        {
            if (current.NextNode != null)
            {
                current = current.NextNode;

                var newNode = new TreeNode(NodeText(current));
                var treeParent = tree.Parent;
                treeParent.Nodes.Add(newNode);
                tree = newNode;
                RefreshTreeView();
            }
            else
            {
                MessageBox.Show("Current node is last sibling.", "Warning",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void locateComboBox_SelectedIndexChanged(
            object sender, EventArgs e)
        {
            // retrieve the set of elements to output
            switch (locateComboBox.SelectedIndex)
            {
                case 0: // print all sports elements
                    PrintElements(document.Elements("sports"));
                    break;
                case 1: // print all game elements
                    PrintElements(document.Descendants("game"));
                    break;
                case 2: // print all name elements
                    PrintElements(document.XPathSelectElements("//name"));
                    break;
                case 3: // print all paragraph elements
                    PrintElements(document.Descendants("game")
                        .Elements("paragraph"));
                    break;
                case 4: // print game elements with name element of "Cricket"
                    // use LINQ to XML to retrieve the correct node
                    var cricket =
                        from game in document.Descendants("game")
                        where game.Element("name").Value == "Cricket"
                        select game;
                    PrintElements(cricket);
                    break;
                case 5: // print all id attributes of game
                    PrintIDs(document.Descendants("game"));
                    break;
            }
        }

        private void PrintElements(IEnumerable<XElement> elements)
        {
            locateTextBox.Clear();

            foreach (var element in elements)
                locateTextBox.AppendText(element.Value.Trim() + '\n');
        }

        private void PrintIDs(IEnumerable<XElement> elements)
        {
            locateTextBox.Clear();

            foreach (var element in elements)
                locateTextBox.AppendText(
                    element.Attribute("id").Value.Trim() + '\n');
        }
    }
}